package com.jweb.framework.ws.server;

import java.io.IOException;
import java.util.Map;

import javax.xml.namespace.QName;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller;
import org.springframework.util.Assert;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.support.MarshallingUtils;

import com.jweb.framework.core.ContextEx;
import com.jweb.framework.core.CoreException;
import com.jweb.framework.core.channel.ChannelContext;
import com.jweb.framework.core.channel.ChannelInterceptor;
import com.jweb.framework.core.channel.interceptors.ExceptionHandler;
import com.jweb.framework.ws.channel.QNameMapping;

/**
 * 锟睫革拷锟剿ｏ拷 Thomas
 * 锟睫革拷锟斤拷锟捷ｏ拷锟斤拷锟斤拷 
 * 锟斤拷说锟斤拷锟斤拷webServices 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷
 */
public class MarshallingInterceptor<Request , Response> implements ChannelInterceptor<Request, Response> {
	
	private static final Logger logger = LoggerFactory.getLogger(MarshallingInterceptor.class) ;
	private Unmarshaller unmarshaller ;
	private Marshaller marshaller;
	private QNameMapping identityMapping ;
	private ExceptionHandler exceptionHandler ;
	
	
	public void setUnmarshaller(Unmarshaller unmarshaller) {
		this.unmarshaller = unmarshaller ;
		if(unmarshaller instanceof Marshaller){
			this.marshaller = (Marshaller) unmarshaller;
		}
	}
	
	public void setMarshaller(Marshaller marshaller){
		this.marshaller = marshaller ;
		if(marshaller instanceof Unmarshaller){
			this.unmarshaller = (Unmarshaller) marshaller ;
		}
	}
	
	public void setExceptionHandler(ExceptionHandler exceptionHandler){
		this.exceptionHandler = exceptionHandler ;
	}
	
	public void setIdentityMapping(QNameMapping qnameMapping){
		this.identityMapping = qnameMapping ;
	}
	
	/**
	 * 锟斤拷锟斤拷
	 */
	@Override
	public void onRequest(ChannelContext<Request, Response> channelContext , ContextEx ctxEx) throws CoreException {
		
		WebServiceMessage webServiceMessage = ((MessageContext)channelContext.getRequest()).getRequest(); 
		Map<String , Object> map ;
		try {
			map = (Map<String, Object>) wsToMap(webServiceMessage) ;
		} catch (IOException e) {
			throw new CoreException("sdo_unmarshaller_error" , e);
		}
		
		if(logger.isDebugEnabled()){
			logger.debug("request data : " + map) ;
		}
		
		QName qname = (QName) map.remove("QNameMapping.current");
		ctxEx.setAttribute("QNameMapping.current" , qname);
		
		String processId = this.identityMapping.toIdentity(qname);
		if(processId != null){
			if(logger.isDebugEnabled()){
				logger.debug("mapping QName to identity: " + qname + " -> " + processId);
			}
			ctxEx.setProcessId(processId);
		}
		else {
			throw new IllegalStateException("No Identity mapped by QNameMapping for " + qname) ;
		}
		
		channelContext.setRequestPayload(map) ;
	}
	
	private Object wsToMap(WebServiceMessage webServiceMessage) throws IOException {
		Unmarshaller unmarshaller = this.unmarshaller ;
		Assert.notNull(unmarshaller,  "No unmarshaller registered . Check configuration of endpoint . ");
		Object obj = MarshallingUtils.unmarshal(unmarshaller, webServiceMessage);
		if(logger.isDebugEnabled()){
			logger.debug("Unmarshalled payload request to [" + obj + "]");
		}
		
		return obj ;
	}
	
	
	/**
	 * 锟斤拷应
	 */
	@Override
	public void onResponse(ChannelContext<Request, Response> channelContext , ContextEx ctxEx, Throwable throwable) {
		
		if(this.exceptionHandler != null){
			this.exceptionHandler.handleException(throwable, ctxEx);
			channelContext.setResponsePayload(ctxEx.getDataMap());
		}
		
		Map<String , Object> map = (Map<String, Object>) channelContext.getResponsePalyload() ;
		
		if(map == null){
			return ;
		}
		
		if(logger.isDebugEnabled()){
			logger.debug("response data : " + map) ;
		}
		
		QName qname = ctxEx.getAttribute("QNameMapping.current");
		map.put("QNameMapping.current", qname);
		
		int i = (map.get("ServiceManager.Oneway") != null) ? 1 : 0 ;
		if(i == 0){
			WebServiceMessage webServiceMessage = ((MessageContext)channelContext.getResponse()).getResponse() ;
			try {
				mapToWs(map , webServiceMessage) ;
			} catch (IOException e) {
				logger.error("sdo_marshal_error" , e) ;
			}
		}
		
	}
	
	
	private void mapToWs(Map<String , Object> map , WebServiceMessage webServiceMessage) throws IOException {
		
		Marshaller marshaller = this.marshaller ;
		Assert.notNull(marshaller , "No marshaller registered . Check configuration of endpoint . ");
		
		if(logger.isDebugEnabled()){
			logger.debug("Marshalling [" + map + "] to response payload");
		}
		
		MarshallingUtils.marshal(marshaller, map , webServiceMessage) ;
	}
	
	
}
